#include <net/net.h>
#include <core/errno.h>
#include <malloc.h>
#include <g-bios.h>
#include <arch/arm.h>


#define MAX_SOCK_FDS  32

static struct Socket *g_pSockpDesc[MAX_SOCK_FDS];

struct Socket *GetSock(int fd);

//
int socket(int domain, int type, int protocol)
{
	int fd;
	struct Socket *pSock;

	for (fd = 1; fd < MAX_SOCK_FDS; fd++)
	{
		if (NULL == g_pSockpDesc[fd])
			goto alloc_sock;
	}

	return -ENODEV;

alloc_sock:
	pSock = malloc(sizeof(struct Socket));

	if (NULL == pSock)
	{
		printf("%s: fail to alloc socket!\n", __FUNCTION__);
		return -ENOMEM;
	}

	memset(&pSock->addr, 0, sizeof(struct sockaddr));

	InitListHead(&pSock->quSend);
	InitListHead(&pSock->quRecv);

	g_pSockpDesc[fd] = pSock;

	return fd;
}

// fix for full destroy
int close(int fd)
{
	if (fd < 1 || fd >= MAX_SOCK_FDS || NULL == g_pSockpDesc[fd])
	{
		printf("%s: invalid fd!\n", __FUNCTION__);
		return -EINVAL;
	}

	SAFE_FREE(g_pSockpDesc[fd]);

	return 0;
}


struct sockaddr *gethostaddr(const char *sip)
{
	UINT32 nip;
	struct sockaddr *sockAddr;

	if(GuStrToIp((BYTE *)&nip, sip))
	{
		printf("%s(): fail to convert ip %s\n", __FUNCTION__, sip);
		return NULL;
	}

	ArpSendPacket((BYTE *)&nip, NULL, ARP_OP_REQ);

	while (1)
	{
		UINT32 psr;

		sockAddr = getaddr(nip);	
		if (sockAddr)
			break;

		// TODO: add re-send code here
		udelay(3);
	}

	return sockAddr;
}

int connect(int fd, const struct sockaddr *addr, socklen_t len)
{
	struct Socket *pSock;

	pSock = GetSock(fd);

	if (NULL == pSock)
	{
		// printf
		return -EINVAL;
	}

	memcpy(&pSock->addr, addr, len);

	return 0;
}

//
long send(int fd, const void *buf, ULONG n)
{
	struct Socket *pSock;
	struct SocketBuffer *pSockBuff;

	pSock = GetSock(fd);

	if (NULL == pSock)
	{
		// printf
		return -EINVAL;
	}

	pSockBuff = GkSockBuffAlloc(ETH_HDR_LEN + IP_HDR_LEN + UDP_HDR_LEN, n);

	pSockBuff->pSock = pSock;

	memcpy(pSockBuff->pSkbData, buf, n);

	GUdpSendPacket(pSockBuff);

	return n;
}

//
long recv(int fd, void *buf, ULONG n)
{
	struct Socket *pSock;
	struct SocketBuffer *pSockBuff;
	ULONG nPktLen;

	pSock = GetSock(fd);

	if (NULL == pSock)
	{
		return -EINVAL;
	}

	pSockBuff = GUdpRecvPacket(pSock);

	// fixme !
	nPktLen   = pSockBuff->wSkbSize <= n ? pSockBuff->wSkbSize : n;

	memcpy(buf, pSockBuff->pSkbData, nPktLen);

	GkSockBuffFree(pSockBuff);

	return nPktLen;
}


struct Socket *GetSock(int fd)
{
	if (fd < 1 || fd >= MAX_SOCK_FDS)
		return NULL;

	return g_pSockpDesc[fd];
}

struct Socket *SearchSocket(const struct UDPHeader *pUdpPkt, const struct IPHeader *pIpPkt)
{
	int fd;
	struct Socket *pSock;
	struct sockaddr *pSockAddr;
	UINT32 dwSrcIP;


	GuNetGetIP(NULL, &dwSrcIP);

	for (fd = 1; fd < MAX_SOCK_FDS; fd++)
	{
		pSock = g_pSockpDesc[fd];

		if (NULL == pSock)
			continue;

		pSockAddr = &pSock->addr;

		if (memcmp(pSockAddr->pbDesIP, pIpPkt->pbSrcIP, 4) != 0) 
		{
#if 0
			printf("pSock = 0x%x, des ip is %d (NOT %d.%d.%d.%d, %02x.%02x.%02x.%02x.%02x.%02x)\n",
				pSockAddr,
				pIpPkt->pbSrcIP[3],
				pSockAddr->pbDesIP[0],
				pSockAddr->pbDesIP[1],
				pSockAddr->pbDesIP[2],
				pSockAddr->pbDesIP[3],
				pSockAddr->pbDesMac[0],
				pSockAddr->pbDesMac[1],
				pSockAddr->pbDesMac[2],
				pSockAddr->pbDesMac[3],
				pSockAddr->pbDesMac[4],
				pSockAddr->pbDesMac[5]
				);
#endif
			continue;
		}

		if (pSockAddr->wSrcPort != pUdpPkt->wDesPort)
		{
#if 0
			printf("from %d, src port: 0x%x != 0x%x\n", pIpPkt->pbSrcIP[3], BE16_TO_CPU(pSockAddr->wSrcPort), BE16_TO_CPU(pUdpPkt->wDesPort));
#endif
			continue;
		}

		if (memcmp(&dwSrcIP, pIpPkt->pbDesIP, 4) != 0) 
		{
#if 0
			printf("src ip: %d.%d.%d.%d\n",  
				pIpPkt->pbDesIP[0],
				pIpPkt->pbDesIP[1],
				pIpPkt->pbDesIP[2],
				pIpPkt->pbDesIP[3]
				);
#endif
			continue;
		}

		return pSock;
	}

	return NULL;
}

void InitSocket(void)
{
	int fd;

	g_pSockpDesc[0] = NULL;//(struct Socket *)g_pSockpDesc;

	for (fd = 1; fd < MAX_SOCK_FDS; fd++)
		g_pSockpDesc[fd] = NULL;

}
